package com.syyo.admin.config.security.controller;

import com.syyo.admin.common.anno.AnonymousAccess;
import com.syyo.admin.common.constant.SettingsConstant;
import com.syyo.admin.common.enums.ResultEnum;
import com.syyo.admin.common.exception.SysException;
import com.syyo.admin.config.security.config.SecurityProperties;
import com.syyo.admin.config.security.config.TokenProvider;
import com.syyo.admin.config.security.domain.AuthUser;
import com.syyo.admin.config.security.domain.JwtUser;
import com.syyo.admin.domain.ResultVo;
import com.syyo.admin.feign.MasterAdminFeign;
import com.syyo.admin.utils.*;
import com.syyo.saas.common.domain.CompanyUser;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: wangzhong
 * @Date: 2020/4/27 17:13
 * @Description: 认证接口
 */
@Api(tags = "系统：认证管理")
@RestController
@RequestMapping("/auth")
public class AuthController {

    private final TokenProvider tokenProvider;
    private final SecurityProperties properties;
    private final UserDetailsService userDetailsService;
    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    public AuthController(SecurityProperties properties,
                          AuthenticationManagerBuilder authenticationManagerBuilder,
                          TokenProvider tokenProvider,
                          UserDetailsService userDetailsService) {
        this.properties = properties;
        this.tokenProvider = tokenProvider;
        this.userDetailsService = userDetailsService;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
    }

    @Autowired
    private SettingsConstant settingsConstant;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MasterAdminFeign masterAdminFeign;

    @ApiOperation("用户登录")
    @AnonymousAccess
    @PostMapping("/login")
    public ResultVo login(@RequestBody AuthUser authUser) {
        // 校验空参数
        CheckParamUtils.validate(authUser);
        ValueOperations<String, String> redis = redisTemplate.opsForValue();
        // 查询验证码
        String codeRedis = redis.get(authUser.getUuid());
        String code = authUser.getCode();
        ResultVo<Object> resultVo = new ResultVo<>();
        if (!code.equals(codeRedis)) {
            // 验证码错误
            resultVo.setCode(ResultEnum.E_90005.getCode());
            resultVo.setMessage(ResultEnum.E_90005.getMessage());
            return resultVo;
        }
        // 清除验证码
        redisTemplate.delete(authUser.getUuid());

        String passwordRsa = authUser.getPassword();
        // rsa  解密
        String password = MyRsaUtils.decrypt(passwordRsa, settingsConstant.getPrivateKey());

        String username = authUser.getUsername();

        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(username, password);

        // 调用 loadUserByUsername,因为同一异常处理器捕捉不到异常，所以在这里捕捉
        try {
            Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);

            SecurityContextHolder.getContext().setAuthentication(authentication);
            // 生成令牌
            Map<String, String> map = tokenProvider.createToken(authentication,authUser.getDb());
            String roles = map.get("roles");
            String token = map.get("token");
            redis.set(token, roles, 2, TimeUnit.HOURS);

            //获取jwtUser对象
            final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();

            // 返回 token 与 用户信息
            Map<String, Object> authInfo = new HashMap<String, Object>(2) {{
                put("token", properties.getTokenStartWith() + token);
                put("user", jwtUser);
            }};
            return R.ok(authInfo);
        } catch (BadCredentialsException e) {
            resultVo.setCode(ResultEnum.E_80006.getCode());
            resultVo.setMessage(ResultEnum.E_80006.getMessage());
            return resultVo;
        } catch (InternalAuthenticationServiceException e) {
            resultVo.setCode(ResultEnum.E_80006.getCode());
            resultVo.setMessage(ResultEnum.E_80006.getMessage());
            return resultVo;
        }
    }

    /**
     * 用户退出
     *
     * @param
     * @return
     */
    @ApiOperation("用户退出")
    @AnonymousAccess
    @PostMapping("/logout")
    public ResultVo logout(HttpServletRequest request) {
        //删除redis的token
        return R.ok(1);
    }

    /**
     * info
     *
     * @param
     * @return
     */
    @ApiOperation("用户info")
    @GetMapping("/info")
    public ResultVo getUserInfo() {
        return R.ok(userDetailsService.loadUserByUsername(SecurityUtils.getUsername()));
    }

    /**
     * info
     *
     * @param
     * @return
     */
    @AnonymousAccess
    @ApiOperation("验证码")
    @GetMapping("/code")
    public ResultVo getCode() {
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(2);
        // 获取运算的结果
        String result = captcha.text();
        String uuid = properties.getCodeKey() + UUID.randomUUID();
        // 保存
        ValueOperations<String, String> redis = redisTemplate.opsForValue();
        redis.set(uuid, result, settingsConstant.getLoginCodeExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        Map<String, Object> map = new HashMap<String, Object>(2) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return R.ok(map);
    }

    /**
     * 向saas主服务器获取租户的数据源
     *
     * @param
     * @return
     */
    @PostMapping("/db")
    public ResultVo getDb(@RequestBody CompanyUser companyUser) {
        String db = masterAdminFeign.companyLogin(companyUser);
        if (MyStringUtils.isEmpty(db)){
            //报错
            throw new SysException(ResultEnum.E_90010.getCode(), ResultEnum.E_90010.getMessage());
        }
        return R.ok(db);
    }

}
